#include "public.h"
#include "log.hpp"
#include <sys/socket.h>
#include <arpa/inet.h>

const string default_ip = "0.0.0.0";
const uint16_t default_port = 8080;

class Socket {
    static const int default_backlog = 20;
public:
    Socket() {
        
    }

    ~Socket() {
        close();
    }

    void socket() {
        _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if(_sockfd < 0) {
            lg(FATAL, "socket error: %s", strerror(errno));
            exit(errno);
        }
        lg(INFO, "socket success, _sockfd = %d", _sockfd);
        int yes = 1;
        if (setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {  
            lg(FATAL, "setsockopt error: %s", strerror(errno));
            exit(errno);  
        }
    }

    void bind(uint16_t port = default_port) {
        struct sockaddr_in localhost;
        localhost.sin_family = AF_INET;
        localhost.sin_port = htons(port);
        localhost.sin_addr.s_addr = INADDR_ANY;
        if(::bind(_sockfd, (const struct sockaddr*)&localhost, sizeof(localhost)) < 0) {
            lg(FATAL, "bind error: %s", strerror(errno));
            exit(errno);
        }
        lg(INFO, "bind success");
    }

    void listen() {
        if(::listen(_sockfd, default_backlog) < 0) {
            lg(FATAL, "listen error: %s", strerror(errno));
            exit(errno);
        }
        lg(INFO, "listen success");
    }

    int accept(bool print_client_info = true) {
        struct sockaddr_in client_addr; 
        socklen_t client_len = sizeof(client_addr);
        int newfd = ::accept(_sockfd, (struct sockaddr *)&client_addr, &client_len);
        if(newfd < 0) {
            lg(WARNING, "accept error: %s", strerror(errno));
            return -1;
        }
        if(print_client_info) {
            char ip_buff[INET_ADDRSTRLEN];
            if(inet_ntop(client_addr.sin_family, &client_addr.sin_addr, ip_buff, sizeof(ip_buff)) != nullptr) {
                //lg(INFO, "get a new link, client_ip = %s, clinet_port = %d", ip_buff, ntohs(client_addr.sin_port));
            }
        }
        lg(INFO, "accept success, newfd = %d", newfd);
        return newfd;
    }

    void connect(const string &ip, uint16_t port) {
        struct sockaddr_in serverhost;
        serverhost.sin_family = AF_INET;
        serverhost.sin_port = htons(port);
        
        if(inet_pton(AF_INET, ip.c_str(), &(serverhost.sin_addr)) != 1) {
            lg(FATAL, "inet_pton() failed");
            exit(errno);
        }
        if(::connect(_sockfd, (const struct sockaddr *)&serverhost, sizeof(serverhost)) < 0) {
            lg(FATAL, "connect error: %s", strerror(errno));
            exit(errno);
        }
        lg(INFO, "connect success");
    }

    void close() {
        ::close(_sockfd);
    }

    int get_sockfd() const {
        return _sockfd;
    }
private:
    int _sockfd;
};